chore(fields): narrow the datetime style cast, and make the #7443 changeset accurate for consumers - #7792
Merged
Merged
Conversation
…ngeset accurate for consumers Two follow-ups recorded from the contract review of objectui#7621, neither of which gated its PASS (objectui#7747). The `DateTimeCellRenderer` style cast goes from `as any` to `(field as DateTimeFieldMetadata | undefined)`. Some cast is load-bearing — `FieldMetadata` is a 37-member union and `BaseFieldMetadata` carries no `format`, so the bare read is `TS2339` — but `as any` was wider than the job. `DateTimeFieldMetadata` is exported from `@object-ui/types` and this file already imports from there, so the narrow cast cost nothing. It is type-level only: the emitted expression is unchanged, and the file's `@typescript-eslint/no-explicit-any` warning count drops 46 to 45, the one removed being the line changed here. The `7443-datetime-compact-style.md` changeset claimed every existing cell renders byte-identically. That is true measured in this repository — no in-repo `datetime` field authors a `format` — but a changeset is a release-note input read by consumers who are not this repository, and objectui#7621 made `DateTimeCellRenderer` start reading `field.format`. The sentence now names its own precondition and the measured consequence: an authored `format` other than `'compact'` is neither rejected nor passed through, it silently selects the verbose `formatDateTime` default. The note also states the gap rather than papering over it — `format` has no declared value vocabulary on a datetime field in either `@object-ui/types` or `@objectstack/spec`. Only the prose changed; the frontmatter and its three package names are byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-sam
marked this pull request as ready for review
September 5, 2026 20:27
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7747
Two non-blocking follow-ups recorded from the contract review of #7621. Both are in this one PR because the card carries both; neither ships anything.
Clause-②: noholds and was re-measured, not assumed — see Runtime behaviour is unchanged below.Item 1 — the
#7443changeset's byte-identity sentence, corrected for consumersThe claim was true measured in this repository (no in-repo
datetimefield authors aformat) but a changeset is a release-note input read by consumers who are not this repository, and #7621 madeDateTimeCellRendererstart readingfield.format.What an unrecognised
formatactually does — MEASURED, not assumedThe card flagged that the value domain is unanswered, so it was measured rather than guessed. A temporary harness rendered
DateTimeCellRendererat one instant (2024-07-04T07:00:00.000Z,en-US) across nine authored values. The harness was deleted after the reading and is not part of this PR.format7/4/2024 7:00 am(compact)''7/4/2024 7:00 am(compact)null7/4/2024 7:00 am(compact)'compact'7/4/2024 7:00 am(compact)'not-a-real-style'Jul 4, 2024, 07:00 AM(verbose)'short'Jul 4, 2024, 07:00 AM(verbose)'relative'Jul 4, 2024, 07:00 AM(verbose)'YYYY-MM-DD'Jul 4, 2024, 07:00 AM(verbose)42(a number)Jul 4, 2024, 07:00 AM(verbose)An unrecognised value is neither rejected nor passed through, and it does NOT fall back to
'compact'— it silently selects the verboseformatDateTimedefault.formatDateTimebranches onoptions.style === 'compact'and everything else falls to the default face; the cell's|| 'compact'only catches the falsy values, which is why''andnullstay compact.That matters because before #7621 the cell ignored
fieldentirely and always painted the compact face (git show 81a2eb1fb^:packages/fields/src/index.tsx— it destructuredvalueonly). So a consumer that authored any non-empty, non-'compact'formaton adatetimefield sees its cells change. That is the one visible consequence the changeset did not spell out.The vocabulary gap — and one correction to the card
The card says
@objectstack/spec"declares noformaton datetime fields". Measured, that is not quite right, and the accurate finding is a stronger version of the same point. The spec's field schema does carryformat, on a single flat (non-discriminated) field schema shared by every field type:So
formatexists but is an unconstrained free-form string whose documented examples are for other field types entirely. There is no declared datetime vocabulary in either@object-ui/types(format?: string) or the spec to check a value against. The corrected sentence names that gap instead of papering over it, as the card asked.The diff — prose only, name set unchanged
The gate that governs this file prints the checkability itself.
node scripts/check-changeset-overwrite.mjs, exit 0:declared at baseanddeclares noware identical: all three package names kept, all three bump levels kept, frontmatter byte-for-byte unchanged. Mechanically:This is the "factual correction to prose" case the gate's own history measurement counts among its 19-for-19 legitimate modifications, and it keeps the 18-for-19 name-preservation shape.
Item 2 —
(field as any)?.formatnarrowed to the subtype that declares itSome cast is load-bearing:
FieldMetadatais a 37-member union,BaseFieldMetadatacarries noformat, so a barefield.formatisTS2339. Butas anywas wider than the job —DateTimeFieldMetadatais exported from@object-ui/types(packages/types/src/index.ts:465,format?: stringatfield-types.ts:301) and this file already imports types from there, so the narrow cast cost one word in an existing import.no-explicit-anyis warn-level here, so Lint is green either way; the count still moves and is checkable:Exactly one warning removed, and it is the line changed here. No other
anyin the file was touched and the rule was not raised to error — both are other cards.The narrowing is load-bearing — lit control, not an assertion
as anycould never have gone red, so it measures nothing as a control. Instead the cast was temporarily swapped toBooleanFieldMetadata— exported from the same barrel, extendsBaseFieldMetadata, and adds onlytype, so it genuinely does not carryformat. The import was swapped with it, otherwisetscwould have answeredTS2304(unknown name) and the reading would have measured the import rather than the property check.RED, and it names the exact line and column of the cast. That is the property
as anywas suppressing:tscreally does check.formatagainst the cast type now.Restoration, proven rather than claimed (restore leg is
git checkout HEAD -- ABSOLUTE_PATH, never the bare form, which restores from the index and would hand the mutation back; the script carriestrap restore EXIT INT TERMfor the crash path, but the proof below is the hash, not the trap firing):Runtime behaviour is unchanged (the Clause-② void condition, checked)
The cast is erased at emit; the expression
(field)?.format || 'compact'is byte-for-byte the same JavaScript. The measured table in item 1 was produced after the narrowing and reproduces the pre-existing behaviour on every row,'compact', empty,nulland unrecognised alike. Nothing in this PR makesDateTimeCellRendererread a different value at runtime, so the declaredClause-②: nostands.Changeset
.changeset/7747-datetime-format-cast-narrowing.mdis a new file with empty frontmatter — item 2 touches a released package'ssrc/, soAGENTS.md:161requires a declaration, and a type-level-only change releases nothing. The item 1 correction is a different file and does not satisfy that requirement.Verification
All readings below were taken at the final commit
a2ee8a2, on a clean tree (git status --porcelainempty). Heavy runs went through the container's shared verify lock; wall-clock figures are shared-box seconds, so only the pass/fail counts are quoted.pnpm --filter @object-ui/fields type-check(tsc --noEmit && tsc -p tsconfig.test.json)pnpm exec vitest run packages/fields/(repo root, the AGENTS.md canonical form)node scripts/check-changeset-presence.mjsnode scripts/check-changeset-no-major.mjsmajordeclarednode scripts/check-changeset-fixed.mjsnode scripts/check-changeset-overwrite.mjsnode scripts/check-control-bytes.mjsnode scripts/check-phantom-dependencies.mjs@object-ui/typesis already a declared dependency of@object-ui/fieldsnode scripts/check-package-self-import.mjsnode scripts/check-unreferenced-sources.mjspnpm exec eslint packages/fields/src/index.tsxno-explicit-anyon the changed line)Every gate exit code above was captured with the command redirected to a file before the status was read, never after a pipe.
One out-of-scope finding, filed not fixed
Filed as #7791.
pnpm --filter @object-ui/fields testreports 2 failures inCapabilityMultiSelectField.specParity-6285.test.tsxon this tree — that test setsVOCABULARY_ROOT = '.'(the process cwd), and the package-level script leaves cwd atpackages/fieldswhile--rootonly moves vitest's root. From the repo root the same file is 7 passed (7). It is unrelated to this diff, which touches neither that test nor its subject: the isolated control (cd packages/fieldsplus the package script's own vitest form, same tree) reproduces it with cwd as the only variable, and the same file from the repo root is green. A pristineorigin/maincheckout was not run — the causal argument here is the cwd control, not a baseline comparison. Out of scope for this card, so it was filed rather than fixed.🤖 Generated with Claude Code
https://claude.ai/code/session_01KbJQ1y1J12nZxYzFWhP8Q3
Generated by Claude Code